class MyNode{
    constructor(type,$state='new',...obj){
        MyNode.idGenerator++;
        this.$type=type;
        this.$state=$state;
        this.id = `n${MyNode.idGenerator}`;
        this.isMatched = false;
        this.unknown = false;

        for(let o of obj){

            for(let key of Object.keys(o)){
                let v = o[key];
                if (typeof(v) == 'string'){
                    v = v.unescapeText();
                    try{
                        if(v.startsWith('[')||v.startsWith('{')||v.startsWith('"')){
                            v = JSON.parse(v.replace(/\n/g,'\\n'));
                        }
                    }catch (e){
                        console.error(e);
                        console.error(v);
                    }
                }
               this[key] = v;
            }
        }
        // jQuery.extend(true,this,...obj);
        delete this.user_id;
        // if (typeof(this.file_id)== 'object' ){
        //     this[key] = typeof(v) == 'object' ? v.low : v;
        // }
    }

    getLinks(g){
        return [... new Set(g.edges)].filter(edge => (!edge.$removed) && edge.from === this.id, this);
    }

    toString() {
        let jsonStrs = [`user_id:"${this.id}"`];
        for(let key of Object.keys(this)){
            if(key=="updateTimestamp"){
                jsonStrs.push(`${key}:"${this[key]}"`);
            }else
            if(!/^\$\S+/g.test(key) && key!='id' && key!='db_id' && key!='isMatched' && key != 'unknown'){
                let v = this[key];
                v = angular.toJson(v);
                if(typeof(v) == 'string'){
                    v = v.escapeText();
                }
                jsonStrs.push(`${key}:"${v}"`);
            }
        }
        return `:${this.$type}{${jsonStrs.join(',')}}`;
    }

    toJson(){
        let json = {
            type:this.$type
        };
        for(let key of Object.keys(this)){
            if(key=="updateTimestamp"){
                json[key] = this[key];
            }else
            if(!/^\$\S+/g.test(key) && key!='id' && key!='isMatched' && key != 'unknown'){
                json[key] = this[key];
            }
        }
        return json;
    }

    static nextHop(session,current_node_id){
        return session.run(`start n=node(${current_node_id}) match (n)-[]->(m) return m`).then(result=>{
            return result.records
                .map((record) => record.get(0))
                .map(node => MyNode.fromNeo4jNode(node));
        });
    }

    static previousHop(session,current_node_id){
        return session.run(`start n=node(${current_node_id}) match (m)-[]->(n) return m`).then(result=>{
            return result.records
                .map((record) => record.get(0))
                .map(node => MyNode.fromNeo4jNode(node));
        });
    }

    static getAncestors(session,current_node_id){
        return session.run(`start n=node(${current_node_id}) match (m)-[*]->(n) return m`).then(result=>{
            return result.records
                .map((record) => record.get(0))
                .map(node => MyNode.fromNeo4jNode(node));
        });
    }

    static fromNeo4jNode(neo4jNode){
        //TODO 此处将neo4j返回的节点转换为本地的类型
        let groupType='';
        let db_id = neo4jNode["identity"].toString();
		let id = (neo4jNode['properties']['user_id'] == undefined || neo4jNode['properties']['user_id'] == "undefined") ? db_id: neo4jNode['properties']['user_id'];
        switch (neo4jNode['labels'][0]){
            case 'Fault':
                return new FaultNode('raw',{id: id,db_id:db_id},neo4jNode['properties']);
            case 'Feature':
                return new FeatureNode('raw',{id:id,db_id:db_id},neo4jNode['properties']);
            case 'Reason':
                return new ReasonNode('raw',{id:id,db_id:db_id},neo4jNode['properties']);
            case 'Component':
                return new ComponentNode('raw',{id:id,db_id:db_id},neo4jNode['properties']);
            case "RunningIssue":
                return new RunningIssueNode('raw',{id:id,db_id:db_id},neo4jNode['properties']);
            case 'Solution':
                return new SolutionNode('raw',{id:id,db_id:db_id},neo4jNode['properties']);
            case "Tool":
                return new ToolNode('raw',{id:id,db_id:db_id},neo4jNode['properties']);
            case "Material":
                return new MaterialNode('raw',{id:id,db_id:db_id},neo4jNode['properties']);
            case "FeatureType":
                return new FeatureTypeNode('raw',{id:id,db_id:db_id},neo4jNode['properties']);
            case 'MatchFunction':
                return new MatchFunctionNode('raw',{id:id,db_id:db_id},neo4jNode['properties']);
            case 'FeatureGroup':
                groupType='Feature';
                break;
            case 'ReasonGroup':
                groupType='Reason';
                break;
            case 'ComponentGroup':
                groupType='Component';
                break;
            default:
                return null;
        }
        return new GroupNode(groupType,'raw',{id:id,db_id:db_id,combineType:neo4jNode['properties']['combineType']},neo4jNode['properties']);
    }

    static compare(node1,node2){
        if (node1.$type != node2.$type){
            return node1.$type > node2.$type ? 1 : -1;
        }
        if(node1.outId != node2.outId){
            return node1.outId > node2.outId ? 1 : -1;
        }

        if (node1.getText() != node2.getText()) {
            return node1.getText() > node2.getText() ? 1 : -1;
        }

        return node1.id > node2.id ? 1 : -1;
    }

    getText(){
        return `${this.getTypeName()} ${this.name}`;
    }

    getTextWithId(){
        return `[${this.id}] ${this.getText()}`;
    }


    getTemplate(edge,g){
        return this.getReadonlyTemplate(edge,g);
    }

    getReadonlyTemplate(edge,g){
        return `
<div class="form-group">
    <label class="col-sm-2 control-label">${this.getTypeName()}</label>
    <p class="col-sm-10 form-control-static">${this.name}</p>
 </div>`
    }

    persist(...callbacks){
        let session = neo4jService.getSession();
        if(this.$state=='new'){
            let resultPromise = session.run(`create (n${this.toString()}) return n `);
            resultPromise.then(result => {
                session.close();
                for (let callback of [...callbacks])
                    callback(result);
            });
        }else //if(this.$state=='dirty')
        {
            let sets = '';
            for(let key of Object.keys(this)){
                if(!/^\$\S+/g.test(key) && key!='id' && key!='db_id'){
                    let v = this[key];
                    v = angular.toJson(v);
                    if(typeof(v) == 'string'){
                        v = v.escapeText();
                    }
                    sets=`${sets} set n.${key}="${v}" `
                }
            }

            let resultPromise = session.run(`start n=node(${this.db_id}) ${sets} RETURN n`);
            resultPromise.then(result => {
                session.close();
                for (let callback of [...callbacks])
                    callback(result);
            });
        }
    }
}
MyNode.idGenerator = 0;
